BA: Бизнес-процессы AgroTracker

Published

June 11, 2026

WarningОграничение публичной документации

В открытом доступе представлена демонстрационная версия метода. В настоящей публичной документации отображены не все шаги, технические сценарии и приватные эндпоинты для системы цифровых симуляторов бизнес-процессов.

  • Полная спецификация метода: Будет доступна только во внутреннем контуре разработки (Confluence / Swagger Enterprise).

1 1. Автоматическое распознавание полевых медиаданных (OCR и Voice v2)

Процесс регламентирует бесшовную оцифровку фотографий качества урожая с полей (PyTorch) и голосовых отчетов наемных рабочих (Whisper) через распределенный gRPC v2 контур.

МОДЕРАЦИЯ / DLQ КОНТУР ИИ (OLLAMA) GO ОРКЕСТРАТОР UI ИНТЕРФЕЙС Медиа-отчет готов 1. Стриминг чанков gRPC Client Streaming 2. Агрегатор чанков Сборка в bytes.Buffer 3. PutObject в S3 Загрузка в MinIO Тип? 6. Вызов gRPC v2 ИИ Передача только s3_key 4а. Whisper (Звук) Речевой транскрипт текста 4б. Llama Vision (Фото) OCR-экстракция сущностей 5. Qwen 2.5 (PARSE) Проверка полноты шкал Инцидент: voice_queue Нехватка мер веса/порций БРАК
СКВОЗНОЙ АУДИТ МАСТЕР-СУБД ХОЛДИНГА БЭКЕНД АГРОТРЕКЕРА ИНТЕРФЕЙС РАБОТНИКА Начало смены 1. Запрос на старт смены Выбор теплицы и активности Отказ: Доступ заблокирован Нет прав на данную теплицу 6. Получить статус смены Статус: В_ПРОЦЕССЕ Смена активна 2. Проверить личность Авторизация сотрудника Допуск? 3. Атомарный запуск Регистрация в системе 5. Трансляция в шину Уведомление ИИ-контуров 4а. Открыть рабочую смену Запись в журнал сессий 4б. Фиксация в Event Log Слепок под Process Mining НЕТ ДА

1.1 Пошаговый алгоритм выполнения ИИ-обработки

  1. Шаг 1 (Интерфейс): Наемный рабочий записывает аудио-комментарий или фотографирует собранный урожай. Приложение запускает gRPC Client Streaming на бэкенд Go.
  2. Шаг 2 и 3 (Go Оркестратор): Сервер на лету агрегирует бинарные чанки в bytes.Buffer и асинхронно загружает через метод PutObject в S3-бакет receipts хранилища MinIO.
  3. Шаг 4 и 5 (Контур ИИ): Go-сервис инициирует высокопроизводительный вызов gRPC v2 инференса. Применяется паттерн передачи по ссылке (By-Reference): бэкенд не пересылает тяжелые байты файлов, а передает моделям Whisper (RecognizeSpeech) и Llama Vision (CheckCropHealth) строго текстовый s3_key.
  4. Шаг 5 (Валидация Qwen): Языковая модель Qwen 2.5:3b производит разбор текста и проверяет инвариант достаточности физических параметров (обязательное наличие связки Название + Вес в граммах + Дискретные единицы).
  5. Аварийный выход (Очередь модерации): Если рабочий допустил ошибку или пропустил вес (например, сказал “Собрал два ведра” без указания массы), транзакция признается дефектной, прерывается и перенаправляется в СУБД в таблицу voice_moderation_queue со статусом pending для ручного исправления оператором.

2 2. Процесс пакетного снабжения телеметрии и геофенсинга

Процесс регламентирует мгновенный прием пакетных массивов оффлайн-координат перемещения сотрудников, разгружая реляционную базу данных холдинга.

СКВОЗНОЙ АУДИТ АНАЛИТИКА / DWH КОНТУР ЗАКУПОК (GO) UI РАБОТНИКА Координаты накоплены 1. Внести пакет точек Массив TelemetryRequest Отказ: HTTP 503 Шина недоступна / Кэш в телефоне Очередь обновлена 2-3. Валидировать DTO Проверка структуры и лимитов Данные OK? 4. Пуш в Kafka транзитом Топик: telemetry.batch.v1 5. Лог в ClickHouse Архивация для Forensic-анализа НЕТ ДА
ОКНО БРАКА / DLQ КОНТУР ИИ (НЕЙРОСЕТИ) ОРКЕСТРАТОР GO UI СМАРТФОНА Медиа-отчет готов 1. Отправить медиапоток Потоковый аплоуд файлов Отчет оцифрован Тип? 2. Сохранить в архив Размещение в S3-хранилище 5. Сформировать допуск Зачисление на баланс смены 3а.Whisper (Голос) Речевой транскрипт рапорта 3б.Vision (Фото) OCR анализ здоровья плодов 4. Семантический парсер Проверка полноты шкал Окно брака оператора Нехватка мер веса/порций БРАК

3 Бизнес-правила и ограничения (Важно для аналитиков)

• Запрет отрицательного баланса: Ни один эндпоинт или фоновый ИИ-воркер не имеет права переводить поле weight_grams (или portions) в таблицах инвентаря в значение меньше нуля. Любая транзакция, нарушающая это условие, атомарно откатывается СУБД, возвращая ошибку RowsAffected == 0. • Правило закрытия трассы (Case ID): Идентификатор смены session_id (или item_id продукта) считается открытым и доступным для сквозного майнинга процессов в PM4Py до тех пор, пока совокупный статус сессии равен IN_PROGRESS, а вес собираемого сырья в контуре больше нуля. Записи со статусом CLOSED, CORRUPTED или REJECTED переводятся ETL-алгоритмами в категорию архивного холодного лога (DWH ClickHouse), закрывая трассу для текущего операционного мониторинга.

4 Завершение и закрытие рабочей смены

СЛУЖБА БЕЗОПАСНОСТИ МАСТЕР-СУБД ХОЛДИНГА GO ОРКЕСТРАТОР ИНТЕРФЕЙС РАБОТНИКА День завершен 1. Нажать 'Закрыть смену' Передача статуса CLOSED Расчет завершен 2. Вычитка метаданных Сверка автора сессии Время OK? 3. Посчитать секунды Вычисление total_duration 4. Закрыть смену в базе UPDATE tracking_sessions Аннулировать выплату Статус: CORRUPTED (Фрод) НЕТ (ФРОД) ДА

Автоматический заслон против фрода времени: Если рабочий намеренно перевел часы назад, чтобы обмануть систему и искусственно раздуть рабочее время, шлюз Go поймает нестыковку хронологии, мгновенно заблокирует транзакцию, переведет сессию в статус CORRUPTED и передаст инцидент службе безопасности для штрафных санкций.

5 Приложение

5.1 Процесс №2: Симулятор наемного полевого труда (Цифровые двойники)

Этот процесс описывает автономную генерацию транзакционных намерений (интентов) ИИ-персонами работников холдинга, синхронизированную с распределением Вейбулла и семантическим ИИ-генератором рапортов.

5.1.1 Доменная структура Агро-Твина

Вместо шкал голода и состава холодильника, Событийная модель Процесса №2 оперирует производственными характеристиками: 1. StaticProfile (Профиль сотрудника холдинга): • worker_id — реальный Integer-ключ сотрудника из таблицы user_field_permissions. • experience_class — квалификация (JUNIOR (склонен к браку), MIDDLE, SENIOR (работает быстрее)). • laziness_factor — коэффициент лени (влияет на плотность выбора активности IDLE). • assigned_fields — массив разрешенных полей ([assigned_field_id, secondary_field_id]), полученный круговым сдвигом из СУБД. 2. DynamicState (Производственные шкалы смены): • current_fatigue — уровень физической усталости от 0.0 до 1.0 (растет с каждым часом работы, сбрасывается в CLOSED). • session_status — OFF_DUTY (смена закрыта), IN_PROGRESS (работает на поле). • active_session_uuid — UUID текущей открытой сессии в Go-бэкенде. • time_on_field — количество часов, отработанных в текущую смену.

5.1.2 Матрица переключения состояний Маркова (Агро-Марков)

На каждом часовом тике, продвигаемом Движком, модель Агро-Твина пересчитывает веса рулетки для выбора следующего действия: START_SHIFT (Начать смену), IDLE (Отдых/Перекур), FIELD_WORK (Выполнение разрешенной активности), SUBMIT_REPORT (Голосовой/Фото отчет), CLOSE_SHIFT (Завершить день). Модификаторы весов: • Усталость (fatigue > 0.80): Вероятность продуктивной работы FIELD_WORK штрафуется на -0.50, вероятность ухода на перекур IDLE или закрытия смены CLOSE_SHIFT возрастает на +0.30. • Длительность смены (time_on_field >= 8 часов): Вес действия CLOSE_SHIFT принудительно увеличивается на +0.60, заставляя твина завершить рабочий день.

5.1.3 Инвариант Будущего Времени (Когда и Какое событие)

После того как Марковская цепь выбрала действие, Движок Вейбулла определяет временную координату виртуального будущего — через сколько минут это событие материализуется.

\[ \text{Virtual Delay (Minutes)} = \beta \cdot \left(-\ln(1 - l)\right)^{\frac{1}{\alpha}} \cdot 60 \]

  • Какое событие: Твин выбрал интент SUBMIT_REPORT (наговорить аудио-рапорт).
  • Когда произойдет: Формула Вейбулла выдала задержку в 140 минут. Если сейчас на часах симулятора 10:00, то в топик agent-intents летит намерение совершить оцифровку в виртуальном будущем: 10:00 + 140 мин = 12:20.
  • Замыкание цикла: Планировщик симулятора прокручивает время. В 12:20 Движок извлекает этот интент и пинает внутреннюю ручку публикации, отправляя сырые данные в ИИ-сервис.

5.1.4 Системный промпт ИИ-генератора рапортов (Генерация Payload)

Когда наступает время выполнения интента SUBMIT_REPORT, Симулятор не дергает Go-бэкенд напрямую, а обращается к генеративной LLM (Ollama/Qwen), чтобы она сформировала реалистичный “человеческий” рапорт для тестирования нашего ИИ-конвейера.


действие: SIMULATE_WORKER_TRANSACTION_JSON
Ты — ИИ-генератор транзакционных данных для симулятора наемного труда агрохолдинга. 
Твоя задача — принять параметры текущего стохастического состояния цифрового двойника и вернуть СТРОГО валидный JSON-пакет, соответствующий доменной схеме закупки, готовки или сбора урожая, без какого-либо лишнего текста, markdown-разметки или объяснений.

Входной контекст стохастического события:
- Текущее действие: {{.action_type}} (Принимает: PURCHASE, COOKING, CONSUMPTION, FIELD_WORK)
- Выращиваемая культура на поле: {{.crop_name}} (например: STRAWBERRY, APPLE)
- Квалификация твина: {{.experience_class}} (Принимает: JUNIOR, MIDDLE, SENIOR)
- Класс дохода / Лимиты холдинга: {{.income_class_or_limit}} (например: LOW, HIGH)

ЖЕСТКИЕ ПРАВИЛА ГЕНЕРАЦИИ ВЕЛИЧИН (Влияние параметров):
1. Если действие = PURCHASE (Закупка сырья/инвентаря): Сгенерируй массив "items". В "units" укажи количество упаковок, в "weight_grams" — чистый вес. Запрещено указывать абстрактные "порции" в чеках. Итоговая "total_price" должна зависеть от лимита (HIGH — дорогие товары, LOW — базовые).
2. Если действие = COOKING (Трансформация вещества): Спиши сырье из остатков по UUID и сгенерируй новое готовое многопорционное блюдо на основе культуры (например, "Клубничный джем" для STRAWBERRY), выставив флаг "is_cooked_dish": true.
3. Влияние Квалификации (experience_class): 
   - Если SENIOR: Расчетный вес продукции "weight_grams" стабильный и высокий, в JSON отсутствуют аномалии.
   - Если JUNIOR: Сделай стохастическую ошибку в JSON (например, занули поле веса "weight_grams" при наличии порций или выдай невалидную категорию), чтобы протестировать отработку инварианта достаточности шкал на Go-бэкенде и отправку в Окно брака.

Верни ответ строго в структуре целевого доменного DTO:
{
  "group_id": "family-uuid-777",
  "correlation_id": "TX-SIM-{{.timestamp}}",
  "data": {
    "receipt_no": "SIM-BILL-XXXX",
    "total_price": 0.0,
    "items": [
      {
        "item_id": "uuid-ингредиента-или-товара",
        "name": "Название продукта на русском языке",
        "category": "Категория",
        "portions": 1.0,
        "weight_grams": 500.0,
        "price": 0.0
      }
    ]
  }
}

5.1.5 Как этот промпт закрывает двустороннюю связь?

  1. Движок симулятора считает стохастику Маркова и задержку Вейбулла, определяя: «Работник JUNIOR на поле STRAWBERRY совершил сбор урожая в 14:00» [model.qmd].
  2. Он подставляет эти параметры в промпт и шлет в Ollama/Qwen.
  3. Так как работник JUNIOR, модель Qwen генерирует «дефектный» JSON, где в массиве items указаны порции, но weight_grams: 0.0.
  4. Симулятор забирает этот готовый JSON и шлет его в ручку /inject нашего Go-сервиса.
  5. Наш Go-сервер принимает пакет, прогоняет через бизнес-логику валидации шкал, видит нулевой вес при наличии порций, блокирует автозачисление и честно отправляет транзакцию в voice_moderation_queue (Окно брака), фиксируя инцидент модерации в СУБД.

5.2 Примеры


{
  "group_id": "family-uuid-777-abc",
  "correlation_id": "TX-SIM-178291029400",
  "data": {
    "receipt_no": "SIM-HARVEST-AURORA-01",
    "total_price": 0.0,
    "items": [
      {
        "item_id": "a1b2-c3d4-e5f6-7777",
        "name": "Клубника сортовая (Свежий сбор)",
        "category": "Ягоды",
        "portions": 1.0,
        "weight_grams": 25000.0,
        "price": 0.0
      }
    ]
  }
}

{
  "group_id": "family-uuid-777-abc",
  "correlation_id": "TX-SIM-178291029555",
  "data": {
    "receipt_no": "SIM-HARVEST-ERROR-99",
    "total_price": 0.0,
    "items": [
      {
        "item_id": "f8e7-d6c5-b4a3-8888",
        "name": "Яблоки Симиренко (Сбор в ящики)",
        "category": "Фрукты",
        "portions": 3.0,
        "weight_grams": 0.0, 
        "price": 0.0
      }
    ]
  }
}


{
  "group_id": "family-uuid-999-xyz",
  "correlation_id": "TX-SIM-178291029600",
  "data": {
    "receipt_no": "SIM-BILL-PRO-STORE",
    "total_price": 14500.0,
    "items": [
      {
        "item_id": "tool-4432-bb88",
        "name": "Секатор Садовый Профессиональный (Япония)",
        "category": "Инвентарь",
        "portions": 2.0,
        "weight_grams": 700.0,
        "price": 7250.0
      }
    ]
  }
}

sequenceDiagram
    autonumber
    actor Twin as Virtual Twin (Worker Agent)
    participant SimClock as Simulator Clock (Timeline Engine)
    participant SimKafka as Simulator Kafka (Topic: agent-intents)
    participant SimCons as Simulator Consumer
    participant Qwen as AI Simulator Service (Qwen 2.5)
    participant GoSvc as Core Go Service (Route: /inject)
    participant MainKafka as Corporate Kafka (Topic: receipt-clean-products-topic)

    Note over Twin: Virtual Time Step: T
    Twin->>Twin: 1. Evaluate Behavior (Markov Chain Action: SUBMIT_REPORT)
    Twin->>Twin: 2. Calculate Clock Skew (Weibull Delay = 140 min)
    
    Twin->>SimKafka: 3. Buffer Intent Metadata (Target Time: T + 140)
    Note over SimKafka: Event queued cleanly within<br/>isolated simulation boundaries

    Note over SimClock: Virtual time moves rapidly forward...<br/>Timeline reaches execution boundary: T + 140 min
    SimClock->>SimCons: Trigger Consumer Execution
    SimCons->>SimKafka: 4. Fetch Matured Intent Payload
    SimKafka-->>SimCons: Return Intent Context (worker_id, crop_name, experience_class)

    SimCons->>Qwen: 5. Request Transaction Generation (JSON Prompt)
    Note over Qwen: Evaluates experience_class profile.<br/>If JUNIOR: injects structural flaws (e.g., zero weight).<br/>If SENIOR: builds pristine dataset.
    Qwen-->>SimCons: Return Domain JSON Structure

    Note over SimCons: Payload compiled successfully
    SimCons->>GoSvc: 6. HTTP POST /api/v1/sim-control/inject?topic=receipt-clean-products-topic&session_id=family-uuid-777-abc
    
    GoSvc->>MainKafka: 7. Emit Pristine/Defective Payload to Corporate Bus [6й package grpc]
    MainKafka-->>GoSvc: Record Delivery Ack [6й package grpc]
    GoSvc-->>SimCons: HTTP 200 OK {"status":"EMITTED"}
    Note over Twin: Asynchronous stochastic trajectory fulfilled

6 Приложение 2

6.1 Сквозной чек-лист интеграционного тестирования (E2E Тест-план)

Данный регламент предназначен для инженеров автоматизации тестирования холдинга для верификации мигрированного Go-бэкенда.

Блок 1: Контур сессий и иерархия доступов

• TC-001: Инициализация смены легитимным сотрудником холдинга. • Шаг: Взять тестовый user_id = 15624510 из первой группы СУБД. Отправить POST /api/v1/sessions/tracking с field_id = 2 (теплица, на которую у него есть явные права в user_field_permissions). • Ожидаемый результат: HTTP 201 Created. В таблице tracking_sessions создается строка в статусе IN_PROGRESS, а в tracking_event_log атомарно падает запись START_SESSION с типом данных JSONB. • TC-002: Попытка взлома иерархии доступа. • Шаг: Отправить запрос на старт сессии от имени user_id = 15624510, но передать field_id = 12 (поле чужого агрохолдинга, к которому у сотрудника нет прав). • Ожидаемый результат: HTTP 403 Forbidden, бизнес-логика Go-сервиса блокирует транзакцию, возвращая структурированное доменное исключение. Блок 2: Высоконагруженный шлюз пакетной телеметрии (React Native оффлайн-кэш)

• TC-003: Пакетный сброс координат (SLA шлюза < 5мс). • Шаг: Сгенерировать пакет BatchTelemetryRequest, содержащий массив из 100 географических точек (TelemetryRequest). Отправить POST /api/v1/sessions/tracking/{uuid}/telemetry/batch. • Ожидаемый результат: HTTP 200 OK, processed_points: 100. В СУБД Postgres запросов не происходит (полное отсутствие логов в БД). В Kafka топик telemetry.batch.v1 мгновенно записывается JSON-срез. • TC-004: Устойчивость клиента при аварии шины данных (Fail-Safe Сценарий). • Шаг: Имитировать падение брокера (выполнить docker-compose stop kafka), отправить валидный пакет телеметрии. • Ожидаемый результат: HTTP 503 Service Unavailable. Клиент franz-kafka фиксирует сбой доставки, HTTP-транспорт перехватывает ошибку и дает сигнал мобильному приложению React Native принудительно удерживать точки в локальном кэше телефона до следующей связи. Блок 3: Хронологические инварианты закрытия смен

• TC-005: Корректный расчет длительности смены.

• Шаг: Отправить PATCH /api/v1/sessions/tracking/{session_id} со статусом CLOSED.
• Ожидаемый результат: HTTP 200 OK. В Postgres обновляются поля end_time и total_duration_seconds (разница времени старта и текущего времени вычисляется корректно). В Kafka транслируется событие закрытия в топик telemetry.events.v1.

• TC-006: Перехват аномалии часов телефона (Time Corruption Barrier).

• Шаг: Имитировать сбой системного времени на устройстве (перевести время назад). Отправить PATCH-запрос, в котором текущее время сервера оказывается меньше времени старта сессии (currentTime < session.StartedAt).
• Ожидаемый результат: HTTP 422 Unprocessable Entity. Транзакция блокируется, репозиторий принудительно переводит сессию в аварийный статус CORRUPTED, а событие эвакуируется в специальный топик инцидентов ИБ для разбора службой безопасности.

::: {.cell}

  
  sequenceDiagram
    autonumber
    participant Client as Мобильный Клиент / Симулятор
    participant Nginx as Nginx Proxy (nginx.conf)
    participant MW as AuthMiddleware (middleware.go)
    participant Hnd as TrackerHandler (router.go)
    participant Pub as KafkaPublisher (publisher.go)
    participant Broker as Apache Kafka Broker
  
    Client->>Nginx: HTTP POST /api/v1/sessions/tracking/{id}/telemetry/batch (JSON + JWT)
    note over Nginx: Валидация размера тела (max 50M)<br/>Включение Gzip декомпрессии
    Nginx->>MW: Проксирование потока байт
    
    note over MW: Zero-Overhead парсинг JWT Claims<br/>Проверка срока жизни exp
    alt Токен просрочен или поврежден
        MW-->>Client: HTTP 401 Unauthorized (GOV_AUTH_DENIED)
    else Токен валиден
        note over MW: Наполнение контекста выполнения:<br/>context.WithValue("user_id", claims.UserID)
        MW->>Hnd: Передача управления (ServeHTTP)
    end
  
    Hnd->>Hnd: PathValue("session_id") + json.NewDecoder()
    note over Hnd: Компоновка транзитного пакета kafkaPayload
    Hnd->>Pub: PublishEvent(ctx, "telemetry.batch.v1", sessionID, payloadBytes)
    Pub->>Broker: ProduceSync() (Сжатие Gzip + Партиционирование по key=sessionID)
    
    alt Брокер Кафки временно недоступен
        Broker-->>Hnd: Ошибка сети
        Hnd-->>Client: HTTP 503 Service Unavailable (Смартфон удерживает кэш локально)
    else Публикация успешна
        Broker-->>Hnd: Record Ack
        Hnd-->>Client: HTTP 200 OK (SUCCESS: Пакет принят в очередь)
    end
  

:::